// The server_example shows how to write write a game server with Rpc service and event/frame processes.

package main

import (
	"flag"
	"fmt"
	"github.com/golang/protobuf/proto"
	"go.uber.org/zap"
	ftProto "gse/exmaples/jmfighter_server/protos"
	"gse/server"
	"gse/server/tcp"
	"gse/server/udp"
	"net/http"
	_ "net/http/pprof"
	"os"
	"strings"
	"time"
	"sync"
)

var logger *zap.Logger

var asyncMode bool
var reqN int
var concurrency int
var srv server.GSEServer
var transportProtocol string
// 攻击陨石游戏组
var pgNPCK *server.EventPushGroup
// map[int32]*server.Session
var pgNPCKRegs *sync.Map = new(sync.Map)
// 对战游戏组
var pgPK *server.EventPushGroup
// map[int32]*server.Session
var pgPKRegs *sync.Map = new(sync.Map)

var playerEventsCh = make(chan *ftProto.PlayerEvent, 1000)

func main() {
	flagset := flag.NewFlagSet("parameters", flag.ExitOnError)
	var sAddr string
	flagset.StringVar(&sAddr, "s", "", "server address, e.g. 127.0.0.1:3131")
	flagset.StringVar(&transportProtocol, "t", "udp", "transport type, \"tcp\" or \"udp\"")
	flagset.BoolVar(&asyncMode, "a", false, "requests in asynchronously.")
	flagset.IntVar(&reqN, "n", 10, "number of requests to be sent to each client.")
	flagset.IntVar(&concurrency, "c", 1, "number of concurrent routines for performing requests.")
	flagset.Parse(os.Args[1:])

	go http.ListenAndServe("0.0.0.0:6364", nil)
	transportProtocol = strings.ToLower(transportProtocol)
	switch transportProtocol {
	case "tcp":
	case "udp":
	default:
		fmt.Printf(`Un-supported transport "%v"`+"\r\n", transportProtocol)
		os.Exit(1)
	}
	var sesConfig *server.SessionConfig

	if transportProtocol == "udp" {
		cfg := udp.NewDefaultUDPServerConfig()
		sesConfig = cfg.SesConfig
		if sAddr != "" {
			cfg.BindAddress = sAddr
		}
		cfg.SesConfig.ReqWaitTimeout = time.Second * 6
		cfg.KeepAlive = 3000
		srv = udp.NewUDPServer(cfg)
	} else {
		cfg := tcp.NewDefaultTCPServerConfig()
		sesConfig = cfg.SesConfig
		if sAddr != "" {
			cfg.BindAddress = sAddr
		}
		srv = tcp.NewTCPServer(cfg)
	}

	logger = server.GetLogger()

	srv.RegisterEvent("sync_battle_frames", &server.EventConfig{30})
	sesConfig.RegisterHook(server.AfterCreate(sessionCreateHook))

	pgNPCK = srv.GetEventPusher().CreateGroup("NPC Killing")
	pgPK = srv.GetEventPusher().CreateGroup("Player Killing")
	go GroupPush()
	srv.Serve()
}

func GroupPush() {
	for {
		m := <-playerEventsCh
		if _, ok := pgPKRegs.Load(m.PlayerID); ok{
			pgPK.QueueEvent("sync_battle_frames", m)
		} else if _, ok := pgNPCKRegs.Load(m.PlayerID); ok {
			pgNPCK.QueueEvent("sync_battle_frames", m)
		} else {
			logger.Warn(fmt.Sprintf("收到未注册分组的玩家消息: %+v", *m))
		}
	}
}

func sessionCreateHook(ses *server.Session, args ...interface{}) {
	logger.Debug(fmt.Sprintf("created session(%v): %+v ", ses.GetRemoteAddr(), *ses))
	go func() {
		<-time.After(time.Millisecond * 2)
		subReg, err := ses.SubEvent("sync_battle_frames", &ftProto.PlayerEvent{})
		if err != nil {
			logger.Warn(err.Error())
			return
		}
		ch := subReg.EventChan()
		for {
			p := <-ch
			var messages = p.GetMessages()
			mLen := len(messages)
			for i :=  mLen; i > 0; i-- {
				pEvt := messages[mLen-i].(*ftProto.PlayerEvent)
				if pEvt.JoinInfo != nil {
					switch pEvt.JoinInfo.MatchType {
					case ftProto.MatchType_NPCK:
						pgNPCK.RegSession(ses)
					    pgNPCKRegs.Store(pEvt.PlayerID, ses)
						logger.Debug(fmt.Sprintf("register session(%v): %+v  to group %v", ses.GetRemoteAddr(), *ses, pgNPCK.Name))
					case ftProto.MatchType_PK:
						pgPK.RegSession(ses)
						pgPKRegs.Store(pEvt.PlayerID, ses)
						logger.Debug(fmt.Sprintf("register session(%v): %+v  to group %v", ses.GetRemoteAddr(), *ses, pgPK.Name))
					default:
						logger.Warn(fmt.Sprintf("[remote: %v]非法的游戏加入类型: %+v", ses.GetLocalAddr(), *pEvt.JoinInfo))
					}
				}
				playerEventsCh <- pEvt
			}
			logger.Debug("got push messages", zap.String("msg", fmt.Sprintf("%+v", p.GetMessages())))
		}
	}()
}

func ReqCallback(param proto.Message, err error) {
	logger.Debug(fmt.Sprintf("received async response, %+v, ERR: %v", param, err))
}
